import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
import cls from 'classnames';
import { usePrefixCls } from './usePrefixCls';
import { pickDataProps } from './pickDataProps';
import { isVoidField } from '@formily/core';
import { connect, mapProps } from '@formily/react';
import { useFormLayout, FormLayoutShallowContext } from '@formily/antd';
import { useGridSpan } from '@formily/antd';
import { Tooltip, Popover } from 'antd';
import {
  QuestionCircleOutlined,
  CloseCircleOutlined,
  CheckCircleOutlined,
  ExclamationCircleOutlined,
} from '@ant-design/icons';

export interface IFormItemProps {
  className?: string;
  style?: React.CSSProperties;
  prefixCls?: string;
  label?: React.ReactNode;
  colon?: boolean;
  tooltip?: React.ReactNode;
  layout?: 'vertical' | 'horizontal' | 'inline';
  tooltipLayout?: 'icon' | 'text';
  labelStyle?: React.CSSProperties;
  labelAlign?: 'left' | 'right';
  labelWrap?: boolean;
  labelWidth?: number | string;
  wrapperWidth?: number | string;
  labelCol?: number;
  wrapperCol?: number;
  wrapperAlign?: 'left' | 'right';
  wrapperWrap?: boolean;
  wrapperStyle?: React.CSSProperties;
  fullness?: boolean;
  addonBefore?: React.ReactNode;
  addonAfter?: React.ReactNode;
  size?: 'small' | 'default' | 'large';
  inset?: boolean;
  extra?: React.ReactNode;
  feedbackText?: any[];
  feedbackLayout?: 'loose' | 'terse' | 'popover' | 'none' | (string & {});
  feedbackStatus?: 'error' | 'warning' | 'success' | 'pending' | (string & {});
  feedbackIcon?: React.ReactNode;
  asterisk?: boolean;
  gridSpan?: number;
  bordered?: boolean;
}

type ComposeFormItem = React.FC<IFormItemProps> & {
  BaseItem?: React.FC<IFormItemProps>;
};

const useFormItemLayout = (props: IFormItemProps) => {
  const layout = useFormLayout();
  return {
    ...props,
    layout: props.layout ?? layout.layout ?? 'horizontal',
    colon: props.colon ?? layout.colon,
    labelAlign:
      layout.layout === 'vertical'
        ? props.labelAlign ?? layout.labelAlign ?? 'left'
        : props.labelAlign ?? layout.labelAlign ?? 'right',
    labelWrap: props.labelWrap ?? layout.labelWrap,
    labelWidth: props.labelWidth ?? layout.labelWidth,
    wrapperWidth: props.wrapperWidth ?? layout.wrapperWidth,
    labelCol: props.labelCol ?? layout.labelCol,
    wrapperCol: props.wrapperCol ?? layout.wrapperCol,
    wrapperAlign: props.wrapperAlign ?? layout.wrapperAlign,
    wrapperWrap: props.wrapperWrap ?? layout.wrapperWrap,
    fullness: props.fullness ?? layout.fullness,
    size: props.size ?? layout.size,
    inset: props.inset ?? layout.inset,
    asterisk: props.asterisk,
    bordered: props.bordered ?? layout.bordered,
    feedbackIcon: props.feedbackIcon,
    feedbackLayout: props.feedbackLayout ?? layout.feedbackLayout ?? 'loose',
    tooltipLayout: props.tooltipLayout ?? layout.tooltipLayout ?? 'icon',
  };
};

function useOverflow<Container extends HTMLElement, Content extends HTMLElement>() {
  const [overflow, setOverflow] = useState(false);
  const containerRef = useRef<Container>();
  const contentRef = useRef<Content>();

  useLayoutEffect(() => {
    window.requestAnimationFrame(() => {
      if (containerRef.current && contentRef.current) {
        if (
          contentRef.current.getBoundingClientRect().width >
          containerRef.current.getBoundingClientRect().width
        ) {
          setOverflow(true);
        } else {
          setOverflow(false);
        }
      }
    });
  }, []);

  return {
    overflow,
    containerRef,
    contentRef,
  };
}

const ICON_MAP = {
  error: <CloseCircleOutlined />,
  success: <CheckCircleOutlined />,
  warning: <ExclamationCircleOutlined />,
};

export const BaseItem: React.FC<IFormItemProps> = (props) => {
  const { children, ...others } = props;
  const [active, setActice] = useState(false);
  const formLayout = useFormItemLayout(others);
  const gridSpan = useGridSpan(props.gridSpan);
  const { containerRef, contentRef, overflow } = useOverflow<HTMLDivElement, HTMLLabelElement>();
  const {
    label,
    style,
    layout,
    colon = true,
    addonBefore,
    addonAfter,
    asterisk,
    feedbackStatus,
    extra,
    feedbackText,
    fullness,
    feedbackLayout,
    feedbackIcon,
    inset,
    bordered = true,
    labelWidth,
    wrapperWidth,
    labelCol,
    wrapperCol,
    labelAlign,
    wrapperAlign = 'left',
    size,
    labelWrap,
    wrapperWrap,
    tooltipLayout,
    tooltip,
  } = formLayout;
  const labelStyle = { ...formLayout.labelStyle };
  const wrapperStyle = { ...formLayout.wrapperStyle };

  const [visible, setVisible] = useState(false);

  const errorsMsg = useMemo(() => {
    if (feedbackText?.length) {
      return feedbackText.reduce((acc, item) => {
        const { messages } = item;
        return `${acc}; ${messages?.join(';')}`;
      }, '');
    }

    return null;
  }, [feedbackText]);

  const handleMouseEnter = () => {
    setVisible(!!feedbackText?.length);
  };

  const handleMouseLeave = () => {
    setVisible(false);
  };

  // 固定宽度
  let enableCol = false;
  if (labelWidth || wrapperWidth) {
    if (labelWidth) {
      labelStyle.width = labelWidth === 'auto' ? undefined : labelWidth;
      labelStyle.maxWidth = labelWidth === 'auto' ? undefined : labelWidth;
    }
    if (wrapperWidth) {
      wrapperStyle.width = wrapperWidth === 'auto' ? undefined : wrapperWidth;
      wrapperStyle.maxWidth = wrapperWidth === 'auto' ? undefined : wrapperWidth;
    }
    // 栅格模式
  }
  if (labelCol || wrapperCol) {
    if (!labelStyle.width && !wrapperStyle.width) {
      enableCol = true;
    }
  }

  const prefixCls = usePrefixCls('formily-item', props);
  const formatChildren =
    feedbackLayout === 'popover' ? (
      <Popover
        autoAdjustOverflow
        placement="top"
        content={
          <div
            className={cls({
              [`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
              [`${prefixCls}-help`]: true,
            })}
          >
            {ICON_MAP[feedbackStatus]} {errorsMsg}
          </div>
        }
        visible={visible}
      >
        <span onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
          {children}
        </span>
      </Popover>
    ) : (
      children
    );

  const gridStyles: React.CSSProperties = {};

  if (gridSpan) {
    gridStyles.gridColumnStart = `span ${gridSpan}`;
  }

  const getOverflowTooltip = () => {
    if (overflow) {
      return (
        <div>
          {/*<div>{label}</div>*/}
          <div>{tooltip || label}</div>
        </div>
      );
    }
    return tooltip || label;
  };

  const renderLabelText = () => {
    const labelChildren = (
      <div className={cls(`${prefixCls}-label-content`)} ref={containerRef}>
        {asterisk && <span className={cls(`${prefixCls}-asterisk`)}>{'*'}</span>}
        <label ref={contentRef}>{label}</label>
      </div>
    );

    if ((tooltipLayout === 'text' && tooltip) || overflow) {
      return (
        <Tooltip placement="top" align={{ offset: [0, 10] }} title={getOverflowTooltip()}>
          {labelChildren}
        </Tooltip>
      );
    }
    return labelChildren;
  };

  const renderTooltipIcon = () => {
    if (tooltip && tooltipLayout === 'icon' && !overflow) {
      return (
        <span className={cls(`${prefixCls}-label-tooltip-icon`)}>
          <Tooltip placement="top" align={{ offset: [0, 2] }} title={tooltip}>
            <QuestionCircleOutlined />
          </Tooltip>
        </span>
      );
    }
  };

  const renderLabel = () => {
    if (!label) return null;
    return (
      <div
        className={cls({
          [`${prefixCls}-label`]: true,
          //  [`${prefixCls}-label-tooltip`]: (tooltip && tooltipLayout === 'text') || overflow,
          [`${prefixCls}-item-col-${labelCol}`]: enableCol && !!labelCol,
        })}
        style={labelStyle}
      >
        {renderLabelText()}

        {label !== ' ' && <span className={cls(`${prefixCls}-colon`)}>{colon ? ':' : ''}</span>}
      </div>
    );
  };

  return (
    <div
      {...pickDataProps(props)}
      style={{
        ...style,
        ...gridStyles,
      }}
      className={cls({
        [`${prefixCls}`]: true,
        [`${prefixCls}-layout-${layout}`]: true,
        [`${prefixCls}-${feedbackStatus}`]: !!feedbackStatus,
        [`${prefixCls}-feedback-has-text`]: !!errorsMsg,
        [`${prefixCls}-size-${size}`]: !!size,
        [`${prefixCls}-feedback-layout-${feedbackLayout}`]: !!feedbackLayout,
        [`${prefixCls}-fullness`]: !!fullness || !!inset || !!feedbackIcon,
        [`${prefixCls}-inset`]: !!inset,
        [`${prefixCls}-active`]: active,
        [`${prefixCls}-inset-active`]: !!inset && active,
        [`${prefixCls}-label-align-${labelAlign}`]: true,
        [`${prefixCls}-control-align-${wrapperAlign}`]: true,
        [`${prefixCls}-label-wrap`]: !!labelWrap,
        [`${prefixCls}-control-wrap`]: !!wrapperWrap,
        [`${prefixCls}-bordered-none`]: bordered === false || !!inset || !!feedbackIcon,
        [props.className]: !!props.className,
      })}
      onFocus={() => {
        if (feedbackIcon || inset) {
          setActice(true);
        }
      }}
      onBlur={() => {
        if (feedbackIcon || inset) {
          setActice(false);
        }
      }}
    >
      {renderLabel()}
      <div
        className={cls({
          [`${prefixCls}-control`]: true,
          [`${prefixCls}-item-col-${wrapperCol}`]: enableCol && !!wrapperCol && label,
        })}
      >
        <div className={cls(`${prefixCls}-control-content`)}>
          {addonBefore && <div className={cls(`${prefixCls}-addon-before`)}>{addonBefore}</div>}
          <div
            style={wrapperStyle}
            className={cls({
              [`${prefixCls}-control-content-component`]: true,
              [`${prefixCls}-control-content-component-has-feedback-icon`]: !!feedbackIcon,
            })}
          >
            <FormLayoutShallowContext.Provider value={undefined}>
              {formatChildren}
            </FormLayoutShallowContext.Provider>
            {feedbackIcon && (
              <div className={cls(`${prefixCls}-feedback-icon`)}>{feedbackIcon}</div>
            )}
          </div>
          {addonAfter && <div className={cls(`${prefixCls}-addon-after`)}>{addonAfter}</div>}
        </div>
        {!!errorsMsg && feedbackLayout !== 'popover' && feedbackLayout !== 'none' && (
          <div
            className={cls({
              [`${prefixCls}-${feedbackStatus}-help`]: !!feedbackStatus,
              [`${prefixCls}-help`]: true,
              [`${prefixCls}-help-enter`]: true,
              [`${prefixCls}-help-enter-active`]: true,
            })}
          >
            {errorsMsg}
          </div>
        )}
        {extra && <div className={cls(`${prefixCls}-extra`)}>{extra}</div>}
      </div>
    </div>
  );
};

// 适配
export const FormItem: ComposeFormItem = connect(
  BaseItem,
  mapProps(
    { validateStatus: true, title: 'label', required: true },
    (props, field) => {
      if (isVoidField(field))
        return {
          extra: props.extra || field.description,
        };
      if (!field) return props;
      const takeMessage = () => {
        const split = (messages: any[]) => {
          return messages.reduce((buf, text, index) => {
            if (!text) return buf;
            return index < messages.length - 1 ? buf.concat([text, ', ']) : buf.concat([text]);
          }, []);
        };
        if (field.validating) return;
        if (props.feedbackText) return props.feedbackText;
        if (field.errors.length) return split(field.errors);
        if (field.warnings.length) return split(field.warnings);
        if (field.successes.length) return split(field.successes);
      };

      return {
        feedbackText: takeMessage(),
        extra: props.extra || field.description,
      };
    },
    (props, field) => {
      if (isVoidField(field)) return props;
      if (!field) return props;
      return {
        feedbackStatus:
          field.validateStatus === 'validating'
            ? 'pending'
            : field.decorator[1]?.feedbackStatus || field.validateStatus,
      };
    },
    (props, field) => {
      if (isVoidField(field)) return props;
      if (!field) return props;
      let asterisk = false;
      if (field.required && field.pattern !== 'readPretty') {
        asterisk = true;
      }
      if ('asterisk' in props) {
        asterisk = props.asterisk;
      }
      return {
        asterisk,
      };
    },
  ),
);

FormItem.BaseItem = BaseItem;

export default FormItem;
